<!DOCTYPE html>
<html lang="en-US">
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>VSCode 源码解读：事件系统设计 | 被删的前端游乐场</title>
    <meta name="generator" content="VuePress 1.8.2">
    
    <meta name="description" content="Just playing around">
    
    <link rel="preload" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css" as="style"><link rel="preload" href="/front-end-playground/assets/js/app.1e2670bf.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/2.38d016d1.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/3.e3f029cb.js" as="script"><link rel="preload" href="/front-end-playground/assets/js/38.b5a974c8.js" as="script">
    <link rel="stylesheet" href="/front-end-playground/assets/css/0.styles.6ad2a9ca.css">
  </head>
  <body>
    <div id="app" data-server-rendered="true"><div class="theme-container"><header class="navbar"><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/front-end-playground/" class="home-link router-link-active"><!----> <span class="site-name">被删的前端游乐场</span></a> <div class="links"><div class="search-box"><input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link router-link-active">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav></div></header> <div class="sidebar-mask"></div> <aside class="sidebar"><nav class="nav-links"><div class="nav-item"><a href="/front-end-playground/" class="nav-link">概述</a></div><div class="nav-item"><a href="/front-end-playground/front-end-basic/" class="nav-link router-link-active">前端领域</a></div><div class="nav-item"><a href="/front-end-playground/vue/" class="nav-link">Vue学习</a></div><div class="nav-item"><a href="/front-end-playground/wxapp/" class="nav-link">小程序学习</a></div><div class="nav-item"><a href="/front-end-playground/front-end-others/" class="nav-link">百家齐放</a></div><div class="nav-item"><a href="/front-end-playground/front-end-addon/" class="nav-link">前端的进击</a></div><div class="nav-item"><a href="/front-end-playground/front-end-work/" class="nav-link">前端与工作</a></div><div class="nav-item"><a href="/front-end-playground/faq.html" class="nav-link">FAQ</a></div> <a href="https://github.com/godbasin/front-end-playground" target="_blank" rel="noopener noreferrer" class="repo-link">
    Github
    <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></nav>  <ul class="sidebar-links"><li><section class="sidebar-group collapsable depth-0" style="padding-top:10px;"><div class="kitty-main" data-v-2b653b36><span class="stand" data-v-2b653b36></span> <div class="cat" data-v-2b653b36><div class="body" data-v-2b653b36></div> <div class="head" data-v-2b653b36><div class="ear" data-v-2b653b36></div> <div class="ear" data-v-2b653b36></div></div> <div class="face" data-v-2b653b36><div class="nose" data-v-2b653b36></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div> <div class="whisker-container" data-v-2b653b36><div class="whisker" data-v-2b653b36></div> <div class="whisker" data-v-2b653b36></div></div></div> <div class="tail-container" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36><div class="tail" data-v-2b653b36></div></div></div></div></div></div></div></div></div></div> <p class="sidebar-heading open"><span>前端架构</span> <span class="arrow down"></span></p> <ul class="sidebar-links sidebar-group-items"><li><a href="/front-end-playground/front-end-basic/deep-learning/module-seperate.html" class="sidebar-link">谈谈依赖和解耦</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/trace-stash.html" class="sidebar-link">大型前端项目要怎么跟踪和分析函数调用链</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/build-application.html" class="sidebar-link">前端构建大型应用</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/reactive-programing.html" class="sidebar-link">响应式编程在前端领域的应用</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html" aria-current="page" class="active sidebar-link">VSCode 源码解读：事件系统设计</a><ul class="sidebar-sub-headers"><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html#q1-vs-code-中的事件管理代码在哪" class="sidebar-link">Q1: VS Code 中的事件管理代码在哪？</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html#q2-vs-code-中的事件都包括了哪些能力" class="sidebar-link">Q2: VS Code 中的事件都包括了哪些能力？</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html#q3-vs-code-中的事件的触发和监听是怎么实现的" class="sidebar-link">Q3: VS Code 中的事件的触发和监听是怎么实现的？</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html#q4-项目中的事件是怎么管理的" class="sidebar-link">Q4: 项目中的事件是怎么管理的？</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html#q5-事件满天飞-不会导致性能问题吗" class="sidebar-link">Q5: 事件满天飞，不会导致性能问题吗？</a></li><li class="sidebar-sub-header"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-event.html#q6-上面只销毁了事件触发器本身的资源-那对于订阅者来说-要怎么销毁订阅的-listener-呢" class="sidebar-link">Q6: 上面只销毁了事件触发器本身的资源，那对于订阅者来说，要怎么销毁订阅的 Listener 呢？</a></li></ul></li><li><a href="/front-end-playground/front-end-basic/deep-learning/vscode-ipc.html" class="sidebar-link">VSCode 源码解读：IPC通信机制</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/online-doc-network.html" class="sidebar-link">在线文档的网络层设计思考</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/front-end-performance-analyze.html" class="sidebar-link">补齐Web前端性能分析的工具盲点</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/monitor-and-report.html" class="sidebar-link">前端监控体系搭建</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/why-spreadsheet-app-excited.html" class="sidebar-link">在线Excel项目到底有多刺激</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/task-runner-design.html" class="sidebar-link">如何设计一个任务管理器</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/network-design-responsibility-driven-design.html" class="sidebar-link">在线文档的网络层开发思考--职责驱动设计</a></li><li><a href="/front-end-playground/front-end-basic/deep-learning/network-design-dependency-decoupling.html" class="sidebar-link">在线文档的网络层开发思考--依赖关系梳理</a></li></ul></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>前端深入理解</span> <span class="arrow right"></span></p> <!----></section></li><li><section class="sidebar-group collapsable depth-0" style="padding-top:;"><!----> <p class="sidebar-heading"><span>前端入门</span> <span class="arrow right"></span></p> <!----></section></li></ul> </aside> <main class="page"> <div class="theme-default-content content__default"><p>最近在研究前端大型项目中要怎么管理满天飞的事件、模块间各种显示和隐式调用的问题，本文结合相应的源码分析，记录 VS Code 中的事件管理系统设计。</p> <h1 id="vs-code-事件"><a href="#vs-code-事件" class="header-anchor">#</a> VS Code 事件</h1> <p>看源码的方式有很多种，带着疑问有目的性地看，会简单很多。</p> <h2 id="q1-vs-code-中的事件管理代码在哪"><a href="#q1-vs-code-中的事件管理代码在哪" class="header-anchor">#</a> Q1: VS Code 中的事件管理代码在哪？</h2> <p>一般来说，说到事件，肯定是跟<code>event</code>关键字相关，因此我们直接全局搜一下文件名（VS Code 下快捷键<code>ctrl+p</code>）:
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vscode-event-file-name.png" alt=""></p> <p>一下子就出来了，这个路径是<code>base\common\event.ts</code>的肯定是比较关键的。打开一看，里面比较关键的有两个：<strong>Event</strong>和<strong>Emitter</strong>。</p> <h2 id="q2-vs-code-中的事件都包括了哪些能力"><a href="#q2-vs-code-中的事件都包括了哪些能力" class="header-anchor">#</a> Q2: VS Code 中的事件都包括了哪些能力？</h2> <p>先来看看<code>Event</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 主要定义了一些接口协议，以及相关方法</span>
<span class="token comment">// 使用 namespace 的方式将相关内容包裹起来</span>
<span class="token keyword">export</span> namespace Event <span class="token punctuation">{</span>
	<span class="token comment">// 来看看里面比较关键的一些方法</span>

	<span class="token comment">// 给定一个事件，返回另一个仅触发一次的事件</span>
  <span class="token keyword">export</span> <span class="token keyword">function</span> once<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">// 给定一连串的事件处理功能（过滤器，映射等），每个事件和每个侦听器都将调用每个函数</span>
  <span class="token comment">// 对事件链进行快照可以使每个事件每个事件仅被调用一次</span>
  <span class="token comment">// 以此衍生了 map、forEach、filter、any 等方法此处省略</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> snapshot<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

	<span class="token comment">// 给事件增加防抖</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> debounce<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">,</span> <span class="token function-variable function">merge</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">last<span class="token operator">:</span> <span class="token constant">T</span> <span class="token operator">|</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> event<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">T</span><span class="token punctuation">,</span> delay<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">,</span> leading<span class="token operator">?</span><span class="token operator">:</span> boolean<span class="token punctuation">,</span> leakWarningThreshold<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>

	<span class="token comment">// 触发一次的事件，同时包括触发时间</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> stopwatch<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span>number<span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

	<span class="token comment">// 仅在 event 元素更改时才触发的事件</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> latch<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

	<span class="token comment">// 缓冲提供的事件，直到出现第一个 listener，这时立即触发所有事件，然后从头开始传输事件</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> buffer<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">,</span> nextTick <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">,</span> _buffer<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">=</span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">// 可链式处理的事件，支持以下方法</span>
	<span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IChainableEvent</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
		event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		map<span class="token operator">&lt;</span><span class="token constant">O</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">i<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">O</span><span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">O</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		<span class="token function">forEach</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">i<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		<span class="token function">filter</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> boolean<span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		filter<span class="token operator">&lt;</span><span class="token constant">R</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token function-variable function">fn</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> <span class="token constant">T</span> <span class="token operator">|</span> <span class="token constant">R</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> e is <span class="token constant">R</span><span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">R</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		reduce<span class="token operator">&lt;</span><span class="token constant">R</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token function-variable function">merge</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">last<span class="token operator">:</span> <span class="token constant">R</span> <span class="token operator">|</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> event<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">R</span><span class="token punctuation">,</span> initial<span class="token operator">?</span><span class="token operator">:</span> <span class="token constant">R</span><span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">R</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		<span class="token function">latch</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		<span class="token function">debounce</span><span class="token punctuation">(</span><span class="token function-variable function">merge</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">last<span class="token operator">:</span> <span class="token constant">T</span> <span class="token operator">|</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> event<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">T</span><span class="token punctuation">,</span> delay<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">,</span> leading<span class="token operator">?</span><span class="token operator">:</span> boolean<span class="token punctuation">,</span> leakWarningThreshold<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		debounce<span class="token operator">&lt;</span><span class="token constant">R</span><span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token function-variable function">merge</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">last<span class="token operator">:</span> <span class="token constant">R</span> <span class="token operator">|</span> <span class="token keyword">undefined</span><span class="token punctuation">,</span> event<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token constant">R</span><span class="token punctuation">,</span> delay<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">,</span> leading<span class="token operator">?</span><span class="token operator">:</span> boolean<span class="token punctuation">,</span> leakWarningThreshold<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">R</span><span class="token operator">&gt;</span><span class="token punctuation">;</span>
		<span class="token function">on</span><span class="token punctuation">(</span><span class="token function-variable function">listener</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> any<span class="token punctuation">,</span> thisArgs<span class="token operator">?</span><span class="token operator">:</span> any<span class="token punctuation">,</span> disposables<span class="token operator">?</span><span class="token operator">:</span> IDisposable<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">|</span> DisposableStore<span class="token punctuation">)</span><span class="token operator">:</span> IDisposable<span class="token punctuation">;</span>
		<span class="token function">once</span><span class="token punctuation">(</span><span class="token function-variable function">listener</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> any<span class="token punctuation">,</span> thisArgs<span class="token operator">?</span><span class="token operator">:</span> any<span class="token punctuation">,</span> disposables<span class="token operator">?</span><span class="token operator">:</span> IDisposable<span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token operator">:</span> IDisposable<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
	<span class="token keyword">class</span> <span class="token class-name">ChainableEvent</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token keyword">implements</span> <span class="token class-name">IChainableEvent</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">// 将事件转为可链式处理的事件</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> chain<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>event<span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> IChainableEvent<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">// 来自 DOM 事件的事件</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> fromDOMEventEmitter<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>emitter<span class="token operator">:</span> DOMEventEmitter<span class="token punctuation">,</span> eventName<span class="token operator">:</span> string<span class="token punctuation">,</span> <span class="token function-variable function">map</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter"><span class="token operator">...</span>args<span class="token operator">:</span> any<span class="token punctuation">[</span><span class="token punctuation">]</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token function-variable function">T</span> <span class="token operator">=</span> <span class="token parameter">id</span> <span class="token operator">=&gt;</span> id<span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">// 来自 Promise 的事件</span>
	<span class="token keyword">export</span> <span class="token keyword">function</span> fromPromise<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token operator">=</span> any<span class="token operator">&gt;</span><span class="token punctuation">(</span>promise<span class="token operator">:</span> Promise<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token keyword">undefined</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>我们能看到，<code>Event</code>中主要是一些对事件的处理和某种类型事件的生成。其中，除了常见的<code>once</code>和 DOM 事件等兼容，还提供了比较丰富的事件能力：</p> <ul><li>防抖动</li> <li>可链式调用</li> <li>缓存</li> <li>Promise 转事件</li></ul> <h2 id="q3-vs-code-中的事件的触发和监听是怎么实现的"><a href="#q3-vs-code-中的事件的触发和监听是怎么实现的" class="header-anchor">#</a> Q3: VS Code 中的事件的触发和监听是怎么实现的？</h2> <p>到这里，我们只看到了关于事件的一些功能（参考<code>Event</code>），而事件的触发和监听又是怎么进行的呢？</p> <p>我们可以继续来看<code>Emitter</code>:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 这是事件发射器的一些生命周期和设置</span>
<span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">EmitterOptions</span> <span class="token punctuation">{</span>
	onFirstListenerAdd<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">;</span>
	onFirstListenerDidAdd<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">;</span>
	onListenerDidAdd<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">;</span>
	onLastListenerRemove<span class="token operator">?</span><span class="token operator">:</span> Function<span class="token punctuation">;</span>
	leakWarningThreshold<span class="token operator">?</span><span class="token operator">:</span> number<span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Emitter</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 可传入生命周期方法和设置</span>
	<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token parameter">options<span class="token operator">?</span><span class="token operator">:</span> EmitterOptions</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

	<span class="token comment">// 允许大家订阅此发射器的事件</span>
	<span class="token keyword">get</span> <span class="token function">event</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
		<span class="token comment">// 此处会根据传入的生命周期相关设置，在对应的场景下调用相关的生命周期方法</span>
	<span class="token punctuation">}</span>

	<span class="token comment">// 向订阅者触发事件</span>
	<span class="token function">fire</span><span class="token punctuation">(</span>event<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>

  <span class="token comment">// 清理相关的 listener 和队列等</span>
	<span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>可以看到，<code>Emitter</code>以<code>Event</code>为对象，以简洁的方式提供了事件的订阅、触发、清理等能力。</p> <h2 id="q4-项目中的事件是怎么管理的"><a href="#q4-项目中的事件是怎么管理的" class="header-anchor">#</a> Q4: 项目中的事件是怎么管理的？</h2> <p><code>Emitter</code>似乎有些简单了，我们只能看到单个事件发射器的使用。那各个模块之间的事件订阅和触发又是怎么实现的呢？</p> <p>我们来全局搜一下关键字<code>Emitter</code>:
<img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/vscode-event-global-emitter.jpg" alt=""></p> <p>搜出来很多地方都有使用，我们来看一下第一个：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 这里我们只摘录相关的代码</span>
<span class="token keyword">class</span> <span class="token class-name">WindowManager</span> <span class="token punctuation">{</span>
  <span class="token keyword">public</span> <span class="token keyword">static</span> readonly <span class="token constant">INSTANCE</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WindowManager</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 注册一个事件发射器</span>
	<span class="token keyword">private</span> readonly _onDidChangeZoomLevel <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Emitter</span><span class="token operator">&lt;</span>number<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token comment">// 将该发射器允许大家订阅的事件取出来</span>
	<span class="token keyword">public</span> readonly onDidChangeZoomLevel<span class="token operator">:</span> Event<span class="token operator">&lt;</span>number<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_onDidChangeZoomLevel<span class="token punctuation">.</span>event<span class="token punctuation">;</span>

	<span class="token keyword">public</span> <span class="token function">setZoomLevel</span><span class="token punctuation">(</span>zoomLevel<span class="token operator">:</span> number<span class="token punctuation">,</span> isTrusted<span class="token operator">:</span> boolean<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_zoomLevel <span class="token operator">===</span> zoomLevel<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">return</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>_zoomLevel <span class="token operator">=</span> zoomLevel<span class="token punctuation">;</span>
		<span class="token comment">// 当 zoomLevel 有变更时，触发该事件</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>_onDidChangeZoomLevel<span class="token punctuation">.</span><span class="token function">fire</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_zoomLevel<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>显然，在 VS Code 里，事件的使用方式主要包括：</p> <ul><li>注册事件发射器</li> <li>对外提供定义的事件</li> <li>在特定时机向订阅者触发事件</li></ul> <p>那么，其他地方又是怎样订阅这么一个事件呢？在这个例子中，由于浏览器实例唯一，可以通过挂载全局对象的方式来提供使用：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 对外提供一个调用全局实例的方法</span>
<span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">onDidChangeZoomLevel</span><span class="token punctuation">(</span><span class="token function-variable function">callback</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">zoomLevel<span class="token operator">:</span> number</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token keyword">void</span><span class="token punctuation">)</span><span class="token operator">:</span> IDisposable <span class="token punctuation">{</span>
	<span class="token keyword">return</span> WindowManager<span class="token punctuation">.</span><span class="token constant">INSTANCE</span><span class="token punctuation">.</span><span class="token function">onDidChangeZoomLevel</span><span class="token punctuation">(</span>callback<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>

<span class="token comment">// 其他地方的调用方式</span>
<span class="token keyword">import</span> <span class="token punctuation">{</span> onDidChangeZoomLevel <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">'vs/base/browser/browser'</span><span class="token punctuation">;</span>
<span class="token keyword">let</span> zoomListener <span class="token operator">=</span> <span class="token function">onDidChangeZoomLevel</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 该干啥干啥</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><p>除此之外，我们也可以通过创建实例调用来直接监听相关事件:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">const</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WindowManager</span><span class="token punctuation">(</span>opts<span class="token punctuation">)</span><span class="token punctuation">;</span>
instance<span class="token punctuation">.</span><span class="token function">onDidChangeZoomLevel</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
  <span class="token comment">// 该干啥干啥</span>
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre></div><h2 id="q5-事件满天飞-不会导致性能问题吗"><a href="#q5-事件满天飞-不会导致性能问题吗" class="header-anchor">#</a> Q5: 事件满天飞，不会导致性能问题吗？</h2> <p>习惯使用一些前端框架的小伙伴们肯定比较有经验，我们如果在某个组件里做了事件订阅这样的操作，当组件销毁的时候是需要取消事件订阅的。否则该订阅内容会在内存中一直存在，除了一些异常问题，还可能引起内存泄露。</p> <p>那么，VS Code 里又是怎么处理这样的问题呢？</p> <p>其实我们在全局搜<code>Emitter</code>的时候，也能看到一些地方的使用方式是：</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 只选取局部关键代码摘要</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Scrollable</span> <span class="token keyword">extends</span> <span class="token class-name">Disposable</span> <span class="token punctuation">{</span>
	<span class="token keyword">private</span> _onScroll <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_register</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Emitter</span><span class="token operator">&lt;</span>ScrollEvent<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">public</span> readonly onScroll<span class="token operator">:</span> Event<span class="token operator">&lt;</span>ScrollEvent<span class="token operator">&gt;</span> <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_onScroll<span class="token punctuation">.</span>event<span class="token punctuation">;</span>

	<span class="token keyword">private</span> <span class="token function">_setState</span><span class="token punctuation">(</span>newState<span class="token operator">:</span> ScrollState<span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
		<span class="token keyword">const</span> oldState <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_state<span class="token punctuation">;</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span>oldState<span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>newState<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">return</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
    <span class="token keyword">this</span><span class="token punctuation">.</span>_state <span class="token operator">=</span> newState<span class="token punctuation">;</span>
    <span class="token comment">// 状态变更的时候，触发事件</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>_onScroll<span class="token punctuation">.</span><span class="token function">fire</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_state<span class="token punctuation">.</span><span class="token function">createScrollEvent</span><span class="token punctuation">(</span>oldState<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>这里使用了<code>this._register(new Emitter&lt;T&gt;())</code>这样的方式注册事件发射器，我们能看到该方法继承自<code>Disposable</code>。而<code>Disposable</code>的实现也很简洁:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> abstract <span class="token keyword">class</span> <span class="token class-name">Disposable</span> <span class="token keyword">implements</span> <span class="token class-name">IDisposable</span> <span class="token punctuation">{</span>
  <span class="token comment">// 用一个 Set 来存储注册的事件发射器</span>
	<span class="token keyword">private</span> readonly _store <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">DisposableStore</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

	<span class="token function">constructor</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
		<span class="token function">trackDisposable</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

  <span class="token comment">// 处理事件发射器</span>
	<span class="token keyword">public</span> <span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
		<span class="token function">markTracked</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

		<span class="token keyword">this</span><span class="token punctuation">.</span>_store<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

  <span class="token comment">// 注册一个事件发射器</span>
	<span class="token keyword">protected</span> _register<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token class-name">IDisposable</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>t<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>t <span class="token keyword">as</span> unknown <span class="token keyword">as</span> Disposable<span class="token punctuation">)</span> <span class="token operator">===</span> <span class="token keyword">this</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">Error</span><span class="token punctuation">(</span><span class="token string">'Cannot register a disposable on itself!'</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_store<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>也就是说，每个继承<code>Disposable</code>类都会有管理事件发射器的相关方法，包括添加、销毁处理等。其实我们仔细看看，这个<code>Disposable</code>并不只是服务于事件发射器，它适用于所有支持<code>dispose()</code>方法的对象：</p> <blockquote><p>Dispose 模式主要用来资源管理，资源比如内存被对象占用，则会通过调用方法来释放。</p></blockquote> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">interface</span> <span class="token class-name">IDisposable</span> <span class="token punctuation">{</span>
	<span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">DisposableStore</span> <span class="token keyword">implements</span> <span class="token class-name">IDisposable</span> <span class="token punctuation">{</span>
	<span class="token keyword">private</span> _toDispose <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Set</span><span class="token operator">&lt;</span>IDisposable<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token keyword">private</span> _isDisposed <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span>

  <span class="token comment">// 处置所有注册的 Disposable，并将其标记为已处置</span>
  <span class="token comment">// 将来添加到此对象的所有 Disposable 都将在 add 中处置。</span>
	<span class="token keyword">public</span> <span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_isDisposed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">return</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>

		<span class="token function">markTracked</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>_isDisposed <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

	<span class="token comment">// 丢弃所有已登记的 Disposable，但不要将其标记为已处置</span>
	<span class="token keyword">public</span> <span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token keyword">void</span> <span class="token punctuation">{</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>_toDispose<span class="token punctuation">.</span><span class="token function">forEach</span><span class="token punctuation">(</span><span class="token parameter">item</span> <span class="token operator">=&gt;</span> item<span class="token punctuation">.</span><span class="token function">dispose</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span>_toDispose<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
	<span class="token punctuation">}</span>

  <span class="token comment">// 添加一个 Disposable</span>
	<span class="token keyword">public</span> add<span class="token operator">&lt;</span><span class="token constant">T</span> <span class="token keyword">extends</span> <span class="token class-name">IDisposable</span><span class="token operator">&gt;</span><span class="token punctuation">(</span>t<span class="token operator">:</span> <span class="token constant">T</span><span class="token punctuation">)</span><span class="token operator">:</span> <span class="token constant">T</span> <span class="token punctuation">{</span>
    <span class="token function">markTracked</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 如果已处置，则不添加</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>_isDisposed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token comment">// 报错提示之类的</span>
		<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>
      <span class="token comment">// 未处置，则可添加</span>
			<span class="token keyword">this</span><span class="token punctuation">.</span>_toDispose<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>t<span class="token punctuation">)</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">return</span> t<span class="token punctuation">;</span>
	<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>因此，我们可以看到，在 VS Code 中是这样管理事件的：</p> <ol><li>抹平 DOM 事件等差异，提供标准化的<code>Event</code>和<code>Emitter</code>能力。</li> <li>通过注册<code>Emitter</code>，并对外提供类似生命周期的方法<code>onXxxxx</code>的方式，来进行事件的订阅和监听。</li> <li>通过提供通用类<code>Disposable</code>，统一管理多个事件发射器（或其他资源）的注册、销毁。</li></ol> <h2 id="q6-上面只销毁了事件触发器本身的资源-那对于订阅者来说-要怎么销毁订阅的-listener-呢"><a href="#q6-上面只销毁了事件触发器本身的资源-那对于订阅者来说-要怎么销毁订阅的-listener-呢" class="header-anchor">#</a> Q6: 上面只销毁了事件触发器本身的资源，那对于订阅者来说，要怎么销毁订阅的 Listener 呢？</h2> <p>或许读到这里的时候，你依然有点懵。看上去 VS Code 的<code>Emitter</code>和<code>Event</code>似乎跟常见的实现方式很相似，只是使用的方式有点不一样而已，到底有什么特别的呢？</p> <p>不知道大家注意到了没，在 VS Code 中，注册一个事件发射器、订阅某个事件，都是通过<code>this._register()</code>这样的方式来实现:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token comment">// 1. 注册事件发射器</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Button</span> <span class="token keyword">extends</span> <span class="token class-name">Disposable</span> <span class="token punctuation">{</span>
  <span class="token comment">// 注册一个事件发射器，可使用 this._onDidClick.fire(xxx) 来触发事件</span>
	<span class="token keyword">private</span> _onDidClick <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_register</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Emitter</span><span class="token operator">&lt;</span>Event<span class="token operator">&gt;</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token keyword">get</span> <span class="token function">onDidClick</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> BaseEvent<span class="token operator">&lt;</span>Event<span class="token operator">&gt;</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_onDidClick<span class="token punctuation">.</span>event<span class="token punctuation">;</span> <span class="token punctuation">}</span>
<span class="token punctuation">}</span>

<span class="token comment">// 2. 订阅某个事件</span>
<span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">QuickInputController</span> <span class="token keyword">extends</span> <span class="token class-name">Disposable</span> <span class="token punctuation">{</span>
  <span class="token comment">// 省略很多其他非关键代码</span>
  <span class="token keyword">private</span> <span class="token function">getUI</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
    <span class="token keyword">const</span> ok <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Button</span><span class="token punctuation">(</span>okContainer<span class="token punctuation">)</span><span class="token punctuation">;</span>
    ok<span class="token punctuation">.</span>label <span class="token operator">=</span> <span class="token function">localize</span><span class="token punctuation">(</span><span class="token string">'ok'</span><span class="token punctuation">,</span> <span class="token string">&quot;OK&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token comment">// 注册一个 Disposable，用来订阅某个事件</span>
		<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">_register</span><span class="token punctuation">(</span>ok<span class="token punctuation">.</span><span class="token function">onDidClick</span><span class="token punctuation">(</span><span class="token parameter">e</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
			<span class="token keyword">this</span><span class="token punctuation">.</span>onDidAcceptEmitter<span class="token punctuation">.</span><span class="token function">fire</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
    <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>也就是说当某个类被销毁时，会发生以下事情：</p> <ol><li>它所注册的事件发射器会被销毁，而事件发射器中的 Listener、队列等都会被清空。</li> <li>它所订阅的一些事件会被销毁，订阅中的 Listener 同样会被移除。</li></ol> <p>至于订阅事件的 Listener 是如何被移除的，可参考以下代码:</p> <div class="language-js extra-class"><pre class="language-js"><code><span class="token keyword">export</span> <span class="token keyword">class</span> <span class="token class-name">Emitter</span><span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
  <span class="token keyword">get</span> <span class="token function">event</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">:</span> Event<span class="token operator">&lt;</span><span class="token constant">T</span><span class="token operator">&gt;</span> <span class="token punctuation">{</span>
		<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>_event<span class="token punctuation">)</span> <span class="token punctuation">{</span>
			<span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function-variable function">_event</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token function-variable function">listener</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token parameter">e<span class="token operator">:</span> <span class="token constant">T</span></span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> any<span class="token punctuation">,</span> thisArgs<span class="token operator">?</span><span class="token operator">:</span> any<span class="token punctuation">,</span> disposables<span class="token operator">?</span><span class="token operator">:</span> IDisposable<span class="token punctuation">[</span><span class="token punctuation">]</span> <span class="token operator">|</span> DisposableStore<span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
        <span class="token comment">// 若无队列，则新建一个</span>
				<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>_listeners<span class="token punctuation">)</span> <span class="token punctuation">{</span>
					<span class="token keyword">this</span><span class="token punctuation">.</span>_listeners <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">LinkedList</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token punctuation">}</span>
        <span class="token comment">// 往队列中添加该 Listener，同时返回一个移除该 Listener 的方法</span>
				<span class="token keyword">const</span> remove <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_listeners<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span><span class="token operator">!</span>thisArgs <span class="token operator">?</span> listener <span class="token operator">:</span> <span class="token punctuation">[</span>listener<span class="token punctuation">,</span> thisArgs<span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

        <span class="token keyword">let</span> result<span class="token operator">:</span> IDisposable<span class="token punctuation">;</span>
        <span class="token comment">// 返回一个带 dispose 方法的结果，dispose 执行时会移除该 Listener</span>
				result <span class="token operator">=</span> <span class="token punctuation">{</span>
					<span class="token function-variable function">dispose</span><span class="token operator">:</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">=&gt;</span> <span class="token punctuation">{</span>
						result<span class="token punctuation">.</span>dispose <span class="token operator">=</span> Emitter<span class="token punctuation">.</span>_noop<span class="token punctuation">;</span>
						<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>_disposed<span class="token punctuation">)</span> <span class="token punctuation">{</span>
							<span class="token function">remove</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
						<span class="token punctuation">}</span>
					<span class="token punctuation">}</span>
				<span class="token punctuation">}</span><span class="token punctuation">;</span>
				<span class="token keyword">if</span> <span class="token punctuation">(</span>disposables <span class="token keyword">instanceof</span> <span class="token class-name">DisposableStore</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
					disposables<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>Array<span class="token punctuation">.</span><span class="token function">isArray</span><span class="token punctuation">(</span>disposables<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
					disposables<span class="token punctuation">.</span><span class="token function">push</span><span class="token punctuation">(</span>result<span class="token punctuation">)</span><span class="token punctuation">;</span>
				<span class="token punctuation">}</span>

				<span class="token keyword">return</span> result<span class="token punctuation">;</span>
			<span class="token punctuation">}</span><span class="token punctuation">;</span>
		<span class="token punctuation">}</span>
		<span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>_event<span class="token punctuation">;</span>
  <span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre></div><p>到这里，VS Code 中事件相关的管理的设计也都呈现出来了，包括：</p> <ul><li>提供标准化的<code>Event</code>和<code>Emitter</code>能力</li> <li>通过注册<code>Emitter</code>，并对外提供类似生命周期的方法<code>onXxxxx</code>的方式，来进行事件的订阅和监听</li> <li>通过提供通用类<code>Disposable</code>，统一管理相关资源的注册和销毁</li> <li>通过使用同样的方式<code>this._register()</code>注册事件和订阅事件，将事件相关资源的处理统一挂载到<code>dispose()</code>方法中</li></ul> <h1 id="结束语"><a href="#结束语" class="header-anchor">#</a> 结束语</h1> <p>VS Code 中除了事件的管理，Dispose 模式还体现在各种其他资源的管理，包括插件等。</p> <p>当我们遇到一些问题不知道该怎么解决的时候，可以试着站到巨人的肩膀上，说不定可以看到更多。</p></div> <!----> <footer class="page-edit"><div class="edit-link"><a href="https://github.com/godbasin/front-end-playground/edit/sourcecode/docs/front-end-basic/deep-learning/vscode-event.md" target="_blank" rel="noopener noreferrer">帮阿猪改善此页面！</a> <span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></div> <!----> <blockquote>部分文章中使用了一些网站的截图，如果涉及侵权，请告诉我删一下谢谢~</blockquote> <div style="margin-top:30px;"><div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-sm-0 el-col-md-2 el-col-lg-4" style="padding-left:10px;padding-right:10px;display:block;"><div style="width:1px;height:1px;"></div></div> <div class="el-col el-col-24 el-col-sm-24 el-col-md-18 el-col-lg-16" style="padding-left:10px;padding-right:10px;"><div class="el-card box-card is-always-shadow"><div class="el-card__header"><div class="clearfix"><span>温馨提示喵</span></div></div><div class="el-card__body"> <div class="el-row" style="margin-left:-10px;margin-right:-10px;"><div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="el-image"><div class="image-slot"><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/loading.gif" style="width:100%;"></div><!----></div></div> <div class="el-col el-col-24 el-col-xs-24 el-col-sm-12" style="padding-left:10px;padding-right:10px;"><div class="copyright-text"><div>本文版权归作者所有，欢迎转载，但未经作者同意必须保留此段声明，且在文章页面明显位置给出原文连接，否则保留追究法律责任的权利。</div> <div>出处：被删的前端游乐场</div> <div>作者：<a href="https://github.com/godbasin" target="_blank">被删</a></div></div></div></div></div></div></div></div></div></footer> <div class="page-nav"><p class="inner"><span class="prev">
        ←
        <a href="/front-end-playground/front-end-basic/deep-learning/reactive-programing.html" class="prev">
          响应式编程在前端领域的应用
        </a></span> <span class="next"><a href="/front-end-playground/front-end-basic/deep-learning/vscode-ipc.html">
          VSCode 源码解读：IPC通信机制
        </a>
        →
      </span></p></div>  <div class="gitalk-container theme-default-content"><div id="gitalk-container" class="content"></div></div></main> <div id="kitty-container"><span><div role="tooltip" id="el-popover-4964" aria-hidden="true" class="el-popover el-popper" style="width:undefinedpx;display:none;"><!----><img src="https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/2code2.jpg" class="image"> <div class="text">牡羊猪的猫粮罐</div> </div><span class="el-popover__reference-wrapper"><div id="kitty" style="background:url(https://github-imglib-1255459943.cos.ap-chengdu.myqcloud.com/assets/img/kitty0.svg);"></div></span></span> <div class="el-dialog__wrapper" style="display:none;"><div role="dialog" aria-modal="true" aria-label="牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）" class="el-dialog" style="margin-top:15vh;"><div class="el-dialog__header"><span class="el-dialog__title">牡羊猪是这样渐渐胖成猪的喵（点击图片可以切换噢）</span><button type="button" aria-label="Close" class="el-dialog__headerbtn"><i class="el-dialog__close el-icon el-icon-close"></i></button></div><!----><!----></div></div></div></div><div class="global-ui"></div></div>
    <script src="/front-end-playground/assets/js/app.1e2670bf.js" defer></script><script src="/front-end-playground/assets/js/2.38d016d1.js" defer></script><script src="/front-end-playground/assets/js/3.e3f029cb.js" defer></script><script src="/front-end-playground/assets/js/38.b5a974c8.js" defer></script>
  </body>
</html>
